/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.platformservice.persistence.api;

import org.proteusframework.core.api.IDelegateConsumer;
import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.platformservice.persistence.project.NoSuchProjectException;
import org.proteusframework.platformservice.persistence.project.ProjectInitializationException;

import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.List;

/**
 * Project management specific routines, including property change listener support.
 *
 * @author Tacoma Four
 */
public interface IProjectManager extends IDelegateConsumer
{
    //
    // Project Services
    //

    /**
     * Flag that indicates if the persistence service supports project services.
     *
     * @return true, if the persistence service can manage projects.
     */
    boolean supportsProjectManagement();

    /**
     * Registers a listener to be notified when project properties change.
     *
     * @param projectPropertiesListener Listener to add
     */
    void addPropertyChangeListener(PropertyChangeListener projectPropertiesListener);

    /**
     * Remove the listener so that it is no longer notified of project property changes.
     *
     * @param projectPropertiesListener Listener to remove
     */
    void removePropertyChangeListener(PropertyChangeListener projectPropertiesListener);

    /**
     * Sets the description to the currently opened project.
     *
     * @param description Description, or null if no description is desired
     * @throws IllegalStateException when {@link #isProjectOpen()} is false
     */
    void setCurrentProjectDescription(String description);

    /**
     * Defines a value associated with the given key under the named property group inside of the currently opened
     * project.
     *
     * @param propertyGroup Property group
     * @param key           Property key
     * @param value         Property value
     * @throws IllegalStateException when {@link #isProjectOpen()} is false
     */
    void setCurrentProjectProperty(String propertyGroup, String key, String value);

    /**
     * Create a new project based on the <code>IProjectDescriptor</code>.
     *
     * @param descriptor Project descriptor
     * @throws ProjectInitializationException when a new project couldn't be successfully created
     */
    void create(IProjectDescriptor descriptor) throws ProjectInitializationException;

    /**
     * Open an existing project represented by the <code>IProjectDescriptor</code>.
     *
     * @param descriptor Project descriptor
     * @throws org.proteusframework.platformservice.persistence.project.NoSuchProjectException
     *                                        When the project described cannot be found in the persistence store
     * @throws ProjectInitializationException when the project couldn't be successfully opened
     */
    void open(IProjectDescriptor descriptor) throws NoSuchProjectException, ProjectInitializationException;

    /**
     * Permenantly purges the project represented by the <code>IProjectDescriptor</code>.
     *
     * @param descriptor Project descriptor
     * @throws NoSuchProjectException When the project described cannot be found in the persistence store
     */
    void delete(IProjectDescriptor descriptor) throws NoSuchProjectException;

    /**
     * Represents the current project that the persistence service is operating against.
     *
     * @return Current project the persistence store is working against as an immutable copy, or null if no project
     *         is open
     */
    IProjectDescriptor getCurrentProject();

    /**
     * Flag that identifies if the persistence store is presently working against a project.
     *
     * @return true, if a project is open
     */
    boolean isProjectOpen();

    /**
     * Saves the current project that the persistence store is presently working against.
     *
     * @throws IllegalStateException when no project is open
     */
    void save();

    /**
     * Saves the current project that the persistence store is presently working against under a different descriptor.
     *
     * @param descriptor Descriptor that details out the new project information
     * @throws IllegalStateException          when no project is open
     * @throws ProjectInitializationException when a new project couldn't be successfully created
     */
    void saveAs(IProjectDescriptor descriptor) throws ProjectInitializationException;

    /**
     * Closes the current project that the persistence store is presently working against.
     *
     * @throws IllegalStateException when no project is open
     */
    void close();

    /**
     * Lists all of the valid projects known inside of this persistence store.
     *
     * @return Non-null list of project descriptors
     */
    List<IProjectDescriptor> listProjects();

    /**
     * Identifies the last project that was opened by the persistence store.
     *
     * @return Last project opened
     * @throws org.proteusframework.platformservice.persistence.project.NoSuchProjectException
     *          when the persistence store cannot report which project was last opened
     */
    IProjectDescriptor getLastOpenedProject() throws NoSuchProjectException;

    /**
     * Clear (set to empty) the last opened project path in the preferences.
     */
    void clearLastOpenedProjectPath();

    /**
     * Determines if a project matching the given project descriptor exists within the persistence store. The specific
     * way that a project descriptor is matched is persistent service implementation dependent.
     *
     * @param namespace Namespace
     * @return true, if a project matching the namespace is contained within the persistence store
     */
    boolean doesProjectExist(INamespace namespace);

    /**
     * Attempts to load the project descriptor associated with the given namespace,
     *
     * @param namespace Namespace
     * @return project descriptor
     * @throws NoSuchProjectException when no project matching the namespace is contained within the persistence store
     */
    IProjectDescriptor loadProjectDescriptor(INamespace namespace) throws NoSuchProjectException;

    /**
     * Determines whether or not a given name can be used as a new project name.  If a project with that name already
     * exists with the given name or {@code projectName} is null or empty it returns false
     *
     * @param projectName The name to check for validity
     * @return boolean whether name is valid or not
     */
    boolean isValidProjectName(String projectName);

    /**
     * Accepts a project descriptor serializer visitor. The manager must iterate across each known project
     * descriptor, offering each instance up to the serialization visitor.
     *
     * @param visitor Serialization visitor
     * @throws java.io.IOException when the visitor cannot complete its I/O operations successfully
     */
    void accept(IProjectManagerVisitor visitor) throws IOException;
}
